package edu.bbk.muc.lifetracker.sensormanager;

import android.app.Activity;
import android.content.Context;
import android.location.Criteria;
import android.location.Location;
import android.location.LocationListener;
import android.location.LocationManager;
import android.os.Bundle;
import android.util.Log;
import edu.bbk.muc.lifetracker.common.Configuration;
import edu.bbk.muc.lifetracker.common.SensorsToUi;
import edu.bbk.muc.lifetracker.common.SimpleLocation;

/**
 *  * This class is responsible for collection
 *         of geo location information.
 * @author irenatyler and romankirillov 
 */
public class LocationRecorder extends Recorder {

  /**
   * Listener for this {@link LocationRecorder} events.
   * @author irenatyler and romankirillov
   */
  class LocationSensorListener implements LocationListener {

    @Override
    public void onLocationChanged(Location location) {
      Log.d(getTag(),
          String.format(
              "SensorValues: latitude: %1$s, longitude: %2$s, altitude: %3$s",
              location.getLatitude(), location.getLongitude(),
              location.getAltitude()));
      getSensorsToUi().onLocationChanged(location.getTime(), new SimpleLocation(location));
    }

    // do nothing
    @Override
    public void onProviderDisabled(String provider) {
    }

    @Override
    public void onProviderEnabled(String provider) {
    }

    @Override
    public void onStatusChanged(String provider, int status, Bundle extras) {
    }
  }

  private LocationManager locationMngr;
  Criteria providerCriteria; // criteria for choosing location provider

  private String provider; //location provider
  private static final String TAG = "LocationRecorder";
  private LocationSensorListener listener;

  /**
   * Initialises this and parent class fields.
   * @param currentActivity Base application activity
   * @param callback Callback receiver for all sensor events
   */
  public LocationRecorder(Activity currentActivity, SensorsToUi callback) {
    super(currentActivity, callback);
    Log.i(getTag(), "LocationRecorder created");
  }

  @Override
  public void acceptNewConfiguration(Configuration configuration) {
    if (configuration.isLocationEnabled()) {
      doStart();
    } else {
      doStop();
    }
  }

  @Override
  public void doStart() {
    setUpLocationTracking();
  }

  @Override
  public void doStop() {
    // do nothing
  }

  /**
   * Gets location from this {@link LocationRecorder}'s {@linkLocationManager}.
   * @return location
   */
  public Location getLocation() {
    Location location = getLocationMngr().getLastKnownLocation(getProvider());
    return location;
  }

  /**
   * Gets this {@link LocationRecorder}'s {@linkLocationManager}.
   * @return {@linkLocationManager}
   */
  public LocationManager getLocationMngr() {
    return locationMngr;
  }

  /**
   * Gets this {@link LocationRecorder}'s location provider.
   * @return This {@link LocationRecorder}'s location provider
   */
  public String getProvider() {
    return provider;
  }

  /**
   * Gets {@link Criteria} to choose best Location provider.
   * @return {@link Criteria} to choose best Location provider
   */
  public Criteria getProviderCriteria() {
    return providerCriteria;
  }

  /**
   * Sets this {@link LocationRecorder}'s location manager.
   * @param locationMngr This {@link LocationRecorder}'s location manager
   */
  public void setLocationMngr(LocationManager locationMngr) {
    this.locationMngr = locationMngr;
  }

  /**
   * Sets this {@link LocationRecorder}'s location provider.
   * @param provider This {@link LocationRecorder}'s location provider
   */
  public void setProvider(String provider) {
    this.provider = provider;
  }

  /**
   * Sets criteria to get best location provider.
   * @return set up criteria
   */
  public Criteria setProviderCriteria() {
    Criteria criteria = new Criteria();
    criteria.setAccuracy(Criteria.ACCURACY_FINE);
    // criteria will be relaxed if no provider is found
    // no need for bearing, altitude or speed
    criteria.setAltitudeRequired(false);
    criteria.setBearingRequired(false);
    criteria.setSpeedRequired(false);
    criteria.setPowerRequirement(Criteria.POWER_LOW);
    return criteria;
  }

  /**
   * Sets all variables necessary for location tracking.
   */
  public void setUpLocationTracking() {
    int updateIntervalDistance = 50;
    int updateIntervalTime = 1000; // check every second

    //set location manager
    setLocationMngr((LocationManager) getCurrentActivity().getSystemService(
        Context.LOCATION_SERVICE));
    //set criteria to find best location provider
    providerCriteria = setProviderCriteria();
//set location provider
    setProvider(getLocationMngr().getBestProvider(providerCriteria, true));
    Log.i(getTag(), String.format("Location Provider: %1$s", getProvider()));
   //set updates
    setUpUpdates(getProvider(), updateIntervalTime, updateIntervalDistance);

  }

  /**
   * Gets this {@link LocationRecorder}'s events listener.
   * If there isn't one already - creates it first.
   * @return This {@link LocationRecorder}'s events listener.
   */
  private LocationSensorListener getListener() {
    if (this.listener == null) {
      setListener(new LocationSensorListener());
    }
    return listener;
  }

  /**
   * Sets this {@link LocationRecorder}'s events listener.
   * @param listener This {@link LocationRecorder}'s events listener
   */
  private void setListener(LocationSensorListener listener) {
    this.listener = listener;
  }

  /**
   * Sets up updates for location changes events.
   * @param provider Location provider.
   * @param updateIntervalTime Time interval within which to check updates
   * @param updateIntervalDistance Distance interval within which to check updates
   */
  private void setUpUpdates(String provider, int updateIntervalTime,
      int updateIntervalDistance) {
    getLocationMngr().requestLocationUpdates(provider, updateIntervalTime,
        updateIntervalDistance, getListener());
  }

  @Override
  protected String getTag() {
    return TAG;
  }
}
